package yaml

import (
	"io/ioutil"
	"log"
	"strconv"
	"strings"
)

type item struct {
	level         int
	val           string
	key           string
	childrenIndex []int
}

type Yaml struct {
	data map[string]interface{}
}

func LoadPath(filePath string) *Yaml {
	data, _ := ioutil.ReadFile(filePath)
	content := string(data)
	list := strings.Split(content, "\n")
	ListItems := make([]*item, 0)
	for i := 0; i < len(list); i++ {
		level, key, val := getListItem(list[i])
		hasePrefix := strings.HasPrefix(key, "-")
		if hasePrefix {
			it := &item{
				level: level,
				val:   "",
				key:   "",
			}
			if level != 0 {
				parentIndex := getParentIndex(ListItems, level)
				if parentIndex != -1 {
					ListItems[parentIndex].childrenIndex = append(ListItems[parentIndex].childrenIndex, len(ListItems))
				}
			}
			ListItems = append(ListItems, it)
			level += 2
			key = strings.Replace(key, "- ", "", 1)
		}
		it := &item{
			level: level,
			val:   val,
			key:   key,
		}
		if level != 0 {
			parentIndex := getParentIndex(ListItems, level)
			if parentIndex != -1 {
				ListItems[parentIndex].childrenIndex = append(ListItems[parentIndex].childrenIndex, len(ListItems))
			}
		}
		ListItems = append(ListItems, it)
	}
	mp := getRes(ListItems)
	y := &Yaml{
		data: mp,
	}
	return y
}

func getParentIndex(list []*item, itemLevel int) int {
	for i := len(list) - 1; i >= 0; i-- {
		if list[i].level < itemLevel {
			return i
		}
	}
	return -1
}

func getListItem(content string) (level int, key, val string) {
	level = len(content) - len(strings.TrimLeft(content, " "))
	content = strings.Trim(content, " ")
	index := strings.Index(content, ":")
	key = content[:index]
	val = content[index+1:]
	val = strings.Trim(strings.Trim(strings.Trim(val, " "), "\r"), "\"")
	return
}

func getRes(list []*item) map[string]interface{} {
	mp := make(map[string]interface{})
	for i, item := range list {
		if item.level == 0 {
			mp[item.key] = getChild(i, list)
		}
	}
	return mp
}

func getChild(index int, list []*item) interface{} {
	it := list[index]
	val := it.val
	if len(it.childrenIndex) == 0 {
		return val
	}
	mp := make(map[string]interface{})
	slice := make([]interface{}, 0)
	for _, cindex := range it.childrenIndex {
		it := list[cindex]
		res := getChild(cindex, list)
		if it.key == "" {
			slice = append(slice, res)
		} else {
			mp[it.key] = res
		}
	}
	if len(mp) > 0 {
		return mp
	} else {
		return slice
	}
}

type keyItem struct {
	key      string
	index    int
	hasIndex bool
}

func (y *Yaml) GetString(key string) string {
	str, _ := y.getData(key).(string)
	return str
}

func (y *Yaml) getData(key string) interface{} {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()
	keys := getKeys(key)
	var tmp interface{} = y.data
	for _, k := range keys {
		tmp = tmp.(map[string]interface{})[k.key]
		if k.hasIndex {
			tmp = tmp.([]interface{})[k.index]
		}
	}
	return tmp
}

func (y *Yaml) GetInt(key string) int {
	v := y.getData(key)
	s, _ := v.(string)
	i, _ := strconv.Atoi(s)
	return i
}

func getKeys(key string) []*keyItem {
	keys := make([]*keyItem, 0)
	list := strings.Split(key, ".")
	for _, k := range list {
		listK := strings.Split(k, "[")
		kItem := &keyItem{
			key:      listK[0],
			index:    0,
			hasIndex: false,
		}

		if len(listK) > 1 {
			kItem.hasIndex = true
			kItem.index, _ = strconv.Atoi(strings.Trim(listK[1], "]"))
		}
		keys = append(keys, kItem)
	}
	return keys
}
